home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Archive / Networking / GetZoneList / GetZoneList.p < prev    next >
Encoding:
Text File  |  2000-09-28  |  22.6 KB  |  822 lines  |  [TEXT/MPS ]

  1. {------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    AppleTalk GetZoneList Sample Application
  6. #
  7. #    GetZoneList
  8. #
  9. #    GetZoneList.p    -    Pascal Source
  10. #
  11. #    Copyright © 1988-90 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    1.00                November 1988
  15. #                1.01                October 1989
  16. #                1.02                May 1990
  17. #                1.03                June 1992
  18. #                1.04                July 1992
  19. #
  20. #    Components:    GetZoneList.c        May 1, 1990
  21. #                GetZoneList.p        May 1, 1990
  22. #                GetZoneList.r        May 1, 1990
  23. #                MakeFile            May 1, 1990
  24. #                UFailure.a            November 1, 1988
  25. #                UFailure.h            November 1, 1988
  26. #                UFailure.inc1.p        November 1, 1988
  27. #                UFailure.p            November 1, 1988
  28. #
  29. #    GetZoneList is a sample application that uses
  30. #    AppleTalk ATP and ZIP to obtain a list of zones
  31. #    on an AppleTalk internet.
  32. #
  33. #    GetZoneList also demonstrates using a signal, or
  34. #    failure-catching mechanism to recover from error
  35. #    situations.
  36. #
  37. #    GetZoneList is based on DTS Sample.p. For more
  38. #    description and explanantion of the non-example
  39. #    specific areas of this application, please refer to
  40. #    either Sample.p or TESample.p.
  41. #
  42. ------------------------------------------------------------------------------}
  43.  
  44. PROGRAM GetZoneList;
  45.  
  46. USES
  47.     Types, QuickDraw, Events, Controls, Windows, TextEdit, Dialogs, Fonts, Lists,
  48.     Menus, Resources, Scrap, ToolUtils, 
  49.     OSUtils, Files, Devices, DeskBus, DiskInit, Disks, Errors, Memory, Retrace, SegLoad, Serial,
  50.     ShutDown, Slots, Sound, Start, Timer, AppleTalk, Packages, FixMath, Script, UFailure;
  51.  
  52. CONST
  53.     _WaitNextEvent            = $A860;
  54.     _Unimplemented            = $A89F;
  55.     kSysEnvironsVersion        = 1;
  56.     kOSEvent                = app4Evt;    {event used by MultiFinder}
  57.     kSuspendResumeMessage    = 1;        {high byte of suspend/resume event message}
  58.     kResumeMask                = 1;        {bit of message field for resume vs. suspend}
  59.  
  60.     kCR                = 13;                {carriage return character}
  61.     kENTER            = 3;                {enter character}
  62.     kScrollBarWidth    = 15;                {the width of the scrollbar in the list}
  63.     kListInset        = -1;                {adjustment for list frame}
  64.     kATPTimeOutVal    = 3;                {re-try ATP SendRequest every 3 seconds}
  65.     kATPRetryCount    = 5;                {for five times}
  66.     kZonesSize        = 578;                {size of buffer for zone names}
  67.     kGZLCall        = $08000000;        {GetZoneList indicator}
  68.     kZIPSocket        = 6;                {the Zone Information Protocol socket}
  69.     kMoreZones        = $FF000000;        {mask to see if more zones to come}
  70.     kZoneCount        = $0000FFFF;        {mask to count zones in buffer}
  71.     kHilite            = 1;                {hilite value for button control}
  72.     kDeHilite        = 0;                {dehilite value for button control}
  73.     kHiliteDelay    = 8;                {time in ticks to leave button hilited}
  74.  
  75.     kMinHeap        = (29 * 1024);
  76.     kMinSpace        = (20 * 1024);
  77.  
  78.     sErrStrings        = 128;                {error string STR#}
  79.     eStandardErr    = 1;
  80.     eWrongMachine    = 2;
  81.     eSmallSize        = 3;
  82.     eNoMemory        = 4;
  83.     eAppleTalk        = 5;
  84.     eNoZones        = 6;
  85.  
  86.     rAboutAlert        = 128;                {about alert}
  87.     rZoneDialog        = 129;                {zone list dialog}
  88.     dZoneList        = 2;                {user item that is zone list}
  89.     dDefault        = 3;                {user item that is default indicator}
  90.     rUserAlert        = 130;                {error alert}
  91.  
  92.     rMenuBar        = 128;                {application's menu bar}
  93.  
  94.     mApple            = 128;                {Apple menu}
  95.     iAbout            = 1;
  96.  
  97.     mFile            = 129;                {File menu}
  98.     iNew            = 1;
  99.     iClose            = 4;
  100.     iQuit            = 12;
  101.  
  102.     mEdit            = 130;                {Edit menu}
  103.     iUndo            = 1;
  104.     iCut            = 3;
  105.     iCopy            = 4;
  106.     iPaste            = 5;
  107.     iClear            = 6;
  108.  
  109.     {1.01 - kDITop and kDILeft are used to locate the Disk Initialization dialogs.}
  110.     kDITop        = $0050;
  111.     kDILeft        = $0070;
  112.  
  113. VAR
  114.     gMac                : SysEnvRec;    {set up by Initialize}
  115.     gHasWaitNextEvent    : BOOLEAN;        {set up by Initialize}
  116.     gInBackground        : BOOLEAN;        {maintained by Initialize and DoEvent}
  117.  
  118.     gList                : ListHandle;    {the list to be filled with zone names}
  119.  
  120.  
  121.  
  122. {$S Initialize}
  123. FUNCTION TrapAvailable(tNumber: INTEGER; tType: TrapType): BOOLEAN;
  124.  
  125. {Check to see if a given trap is implemented. This is only used by the
  126.  Initialize routine in this program, so we put it in the Initialize segment.
  127.  The recommended approach to see if a trap is implemented is to see if
  128.  the address of the trap routine is the same as the address of the
  129.  Unimplemented trap.}
  130. {1.02 - Needs to be called after call to SysEnvirons so that it can check
  131.  if a ToolTrap is out of range of a pre-MacII ROM.}
  132.  
  133. BEGIN
  134.     IF (tType = ToolTrap) &
  135.         (gMac.machineType > envMachUnknown) &
  136.         (gMac.machineType < envMacII) THEN BEGIN        {it's a 512KE, Plus, or SE}
  137.         tNumber := BAND(tNumber, $03FF);
  138.         IF tNumber > $01FF THEN                            {which means the tool traps}
  139.             tNumber := _Unimplemented;                    {only go to $01FF}
  140.     END;
  141.     TrapAvailable := NGetTrapAddress(tNumber, tType) <>
  142.                         NGetTrapAddress(_Unimplemented, ToolTrap);
  143. END; {TrapAvailable}
  144.  
  145.  
  146. {$S Main}
  147. PROCEDURE FailOSErrMsg(result, message: INTEGER);
  148. BEGIN
  149.     IF result <> noErr THEN
  150.         Failure(result, message);
  151. END; {SignalOSErrMsg}
  152.  
  153.  
  154. {$S Main}
  155. PROCEDURE FailNILMsg(p: UNIV Ptr; message: INTEGER);
  156. BEGIN
  157.     IF p = NIL THEN
  158.         Failure(memFullErr, message);
  159. END; {FailNILMsg}
  160.  
  161.  
  162. {$S Main}
  163. PROCEDURE AlertUser(error: INTEGER; message: LongInt);
  164.  
  165. {Display an alert to inform the user of an error. Message acts as an
  166.  index into a STR# resource of error messages. If no message is given,
  167.  i.e. = 0, then use a standard message. If error is not noErr then
  168.  display it as well.}
  169.  
  170. VAR
  171.     msg1, msg2    : Str255;
  172.     itemHit        : INTEGER;
  173. BEGIN
  174.     IF message <= 0 THEN message := eStandardErr;
  175.     GetIndString(msg1, sErrStrings, message);
  176.     IF error = noErr THEN
  177.         msg2 := ''
  178.     ELSE
  179.         NumToString(error, msg2);
  180.     ParamText(msg1, msg2, '', '');
  181.     itemHit := Alert(rUserAlert, NIL);
  182. END; {AlertUser}
  183.  
  184.  
  185. {$S Main}
  186. FUNCTION IsDAWindow(window: WindowPtr): BOOLEAN;
  187. BEGIN
  188.     IF window = NIL THEN
  189.         IsDAWindow := FALSE
  190.     ELSE    {DA windows have negative windowKinds}
  191.         IsDAWindow := WindowPeek(window)^.windowKind < 0;
  192. END; {IsDAWindow}
  193.  
  194.  
  195. {$S Main}
  196. FUNCTION IsAppWindow(window: WindowPtr): BOOLEAN;
  197. BEGIN
  198.     IF window = NIL THEN
  199.         IsAppWindow := FALSE
  200.     ELSE    {application windows have windowKinds >= userKind (8) or dialogKind (2)}
  201.         WITH WindowPeek(window)^ DO
  202.             IsAppWindow := (windowKind >= userKind) | (windowKind = dialogKind);
  203. END; {IsAppWindow}
  204.  
  205.  
  206. {$S Main}
  207. PROCEDURE BuildZoneList;
  208.  
  209. {Create the list of zones on the network. Find a bridge to talk to , if one is
  210.  present, then ask it for zone names. Add the names to the list in the dialog.}
  211.  
  212. VAR
  213.     dATPPBptr                    : ATPPBptr;        {the parameter block for GetZoneList call}
  214.     dBDS                        : BDSElement;    {the BDS for GetZoneList call}
  215.     dZones, dCurr                : Ptr;            {the data buffer for GetZoneList call}
  216.     dIndex, dCount, dNode, dNet    : INTEGER;
  217.     ignore                        : INTEGER;
  218.     cSize                        : Point;
  219.     fi                            : FailInfo;
  220.     nodeNetAddress, bridgeNode    : INTEGER;
  221.  
  222.     PROCEDURE CleanUp;
  223.     BEGIN
  224.         IF dATPPBptr <> NIL THEN
  225.             DisposePtr(Ptr(dATPPBptr));                    {get rid of pb block}
  226.         IF dZones <> NIL THEN
  227.             DisposePtr(dZones);                            {and buffer}
  228.     END; {CleanUp}
  229.  
  230.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  231.     BEGIN
  232.         CleanUp;                                        {get rid of allocated junk}
  233.     END;
  234.  
  235. BEGIN
  236.     dATPPBptr := NIL;                                    {init some important variables}
  237.     dZones := NIL;
  238.     CatchFailures(fi, HandleErr);
  239.  
  240.     { get network address of node & node ID of bridge (if any) }
  241.     FailOSErrMsg(GetNodeAddress(ignore, nodeNetAddress), eAppleTalk);
  242.     bridgeNode := GetBridgeAddress;
  243.  
  244.     { test to see if bridge node fails.  If so, no internet. }
  245.     if (bridgeNode = 0) then
  246.         Failure(0, eNoZones);                                    { bail if no zones present }
  247.  
  248.     dATPPBptr := ATPPBptr(NewPtr(SIZEOF(ATPParamBlock)));
  249.     FailNILMsg(dATPPBptr, eNoMemory);
  250.     dZones := NewPtr(kZonesSize);
  251.     FailNILMsg(dZones, eNoMemory);
  252.     WITH dBDS DO BEGIN                                    {set up BDS}
  253.         buffSize := kZonesSize;
  254.         buffPtr := dZones;
  255.         END;
  256.     WITH dATPPBptr^ DO BEGIN                            {set up pb block}
  257.         atpFlags := 0;
  258.  
  259.         addrBlock.aNet := nodeNetAddress;
  260.         addrBlock.aNode := bridgeNode;                    {get node of bridge}
  261.         addrBlock.aSocket := kZIPSocket;                {the socket we want}
  262.         reqLength := 0;
  263.         reqPointer := NIL;
  264.         bdsPointer := @dBDS;
  265.         numOfBuffs := 1;
  266.         timeOutVal := kATPTimeOutVal;
  267.         retryCount := kATPRetryCount;
  268.         END;
  269.     dIndex := 1;
  270.     dCount := 0;
  271.     SetPt(cSize, 0, 0);                                    {we always stuff into first}
  272.     REPEAT
  273.         dATPPBptr^.userData := kGZLCall + dIndex;        {indicate GetZoneList request}
  274.         FailOSErrMsg(PSendRequest(dATPPBptr,
  275.             FALSE), eAppleTalk);                        {send sync request}
  276.         dCount := dCount + BAND(dBDS.userBytes,
  277.                     kZoneCount);                        {find out how many returned}
  278.         dCurr := dZones;                                {put current pointer at start}
  279.         REPEAT                                            {get each zone}
  280.             ignore := LAddRow(1, 0, gList);                {create new cell at start}
  281.             LSetCell(POINTER(ORD4(dCurr) + 1), dCurr^,
  282.                         cSize, gList);                    {stuff in zone}
  283.             dCurr := POINTER(ORD4(dCurr) + dCurr^+1);    {bump up current pointer}
  284.             dIndex := dIndex + 1;                        {increment which zone}
  285.         UNTIL dIndex > dCount;
  286.     UNTIL (BAND(dBDS.userBytes, kMoreZones) <> 0);        {keep going until none left}
  287.     CleanUp;
  288.  
  289.     Success(fi);
  290. END; {BuildZoneList}
  291.  
  292.  
  293. {$S Main}
  294. PROCEDURE BuildZoneListPhase2;
  295.  
  296. {Create the list of zones on the network. Find a bridge to talk to , if one is
  297.  present, then ask it for zone names. Add the names to the list in the dialog.}
  298.  
  299. VAR
  300.     dXPBPBPtr                    : xPPParmBlkPtr;
  301.     dBDS                        : BDSElement;    {the BDS for GetZoneList call}
  302.     dZones, dCurr                : Ptr;            {the data buffer for GetZoneList call}
  303.     dIndex, dCount, dNode, dNet    : INTEGER;
  304.     ignore                        : INTEGER;
  305.     cSize                        : Point;
  306.     fi                            : FailInfo;
  307.     xppDriverRefNum                : INTEGER;
  308.  
  309.     PROCEDURE CleanUp;
  310.     BEGIN
  311.         IF dXPBPBPtr <> NIL THEN
  312.             DisposePtr(Ptr(dXPBPBPtr));                    {get rid of pb block}
  313.         IF dZones <> NIL THEN
  314.             DisposePtr(dZones);                            {and buffer}
  315.     END; {CleanUp}
  316.  
  317.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  318.     BEGIN
  319.         CleanUp;                                        {get rid of allocated junk}
  320.     END;
  321.  
  322. BEGIN
  323.     dXPBPBPtr := NIL;                                    {init some important variables}
  324.     dZones := NIL;
  325.     CatchFailures(fi, HandleErr);
  326.  
  327.     { Get network address of bridge.  If zero, no internet. }
  328.     if (GetBridgeAddress = 0) then
  329.         Failure(0, eNoZones);                                    { bail if no zones present }
  330.  
  331.     { get a hold of the XPP driver reference number-this is the safest way }
  332.     FailOSErrMsg(OpenDriver('.XPP', xppDriverRefNum), eAppleTalk);
  333.  
  334.     dXPBPBPtr := xPPParmBlkPtr(NewPtr(SIZEOF(xPPParamBlock)));
  335.     FailNILMsg(dXPBPBPtr, eNoMemory);
  336.     dZones := NewPtr(kZonesSize);
  337.     FailNILMsg(dZones, eNoMemory);
  338.  
  339.     dXPBPBPtr^.zipInfoField[1] := 0;    { ALWAYS 0 on first call.  has state info on subsequent calls }
  340.     dXPBPBPtr^.zipInfoField[2] := 0;    { ALWAYS 0 on first call.  has state info on subsequent calls }
  341.     dXPBPBPtr^.zipLastFlag := 0;
  342.  
  343.     dXPBPBPtr^.ioRefNum := xppDriverRefNum;
  344.     dXPBPBPtr^.csCode := xCall;
  345.     dXPBPBPtr^.xppSubCode := zipGetZoneList;
  346.     dXPBPBPtr^.xppTimeOut := kATPTimeOutVal;
  347.     dXPBPBPtr^.xppRetry := kATPRetryCount;
  348.     dXPBPBPtr^.zipBuffPtr := Ptr( dZones);
  349.  
  350.     dIndex := 1;
  351.     dCount := 0;
  352.     SetPt(cSize, 0, 0);                                    {we always stuff into first}
  353.     REPEAT
  354.         FailOSErrMsg(PBControl(ParmBlkPtr (dXPBPBPtr), false), eAppleTalk);        { send sync control call }
  355.         dCount := dCount + dXPBPBPtr^.zipNumZones;                        { find out how many returned }
  356.  
  357.         dCurr := dZones;                                {put current pointer at start}
  358.         REPEAT                                            {get each zone}
  359.             ignore := LAddRow(1, 0, gList);                {create new cell at start}
  360.             LSetCell(POINTER(ORD4(dCurr) + 1), dCurr^,
  361.                 cSize, gList);                            {stuff in zone}
  362.             dCurr := POINTER(ORD4(dCurr) + dCurr^+1);    {bump up current pointer}
  363.             dIndex := dIndex + 1;                        {increment which zone}
  364.         UNTIL dIndex > dCount;
  365.     UNTIL (dXPBPBPtr^.zipLastFlag <> 0);        {keep going until none left}
  366.     CleanUp;
  367.  
  368.     Success(fi);
  369. END; {BuildZoneListPhase2}
  370.  
  371.  
  372. {$S Main}
  373. PROCEDURE ZoneListDraw(dlg: DialogPtr; item: INTEGER);
  374.  
  375. {The user item procedure for the zone list user item and default
  376.  box user item in the dialog. Draw the list and the frame that goes with it.
  377.  Draw the default box around the OK button.}
  378.  
  379. VAR
  380.     port    : GrafPtr;
  381.     kind    : INTEGER;
  382.     h        : Handle;
  383.     r        : Rect;
  384.     ps        : PenState;
  385.  
  386. BEGIN
  387.     GetPort(port);                                    {save old port}
  388.     SetPort(dlg);                                    {make dialog port}
  389.     CASE item OF
  390.     dZoneList: BEGIN
  391.             LUpdate(dlg^.visRgn, gList);            {re-draw list}
  392.             GetDialogItem(dlg, dZoneList, kind, h, r);
  393.             InsetRect(r, kListInset, kListInset);
  394.             FrameRect(r);                            {re-draw frame}
  395.         END;
  396.     dDefault: BEGIN
  397.             GetDialogItem(dlg, dDefault, kind, h, r);
  398.             GetPenState(ps);
  399.             PenNormal;                                {always be on the defensive}
  400.             PenSize(3, 3);
  401.             InsetRect(r, -4, -4);
  402.             FrameRoundRect(r, 16, 16);                {draw default box}
  403.             SetPenState(ps);
  404.         END;
  405.     END;
  406.     SetPort(port);                                    {restore old port}
  407. END; {ZoneListDraw}
  408.  
  409.  
  410. {$S Main}
  411. FUNCTION ListFilter (dlg: DialogPtr; VAR event: EventRecord;
  412.                                             VAR item: INTEGER) : BOOLEAN;
  413.  
  414. {Passed as parameter to ModalDialog. Handle key presses and mouse clicks
  415.  from the user. Do all the right default actions since we override them
  416.  by virtue of our existence.}
  417.  
  418. VAR
  419.     port        : GrafPtr;
  420.     loc            : Point;
  421.     kind        : INTEGER;
  422.     h            : Handle;
  423.     r            : Rect;
  424.     ignore        : BOOLEAN;
  425.     key            : SignedByte;
  426.     finalTicks    : LongInt;
  427. BEGIN
  428.     ListFilter := FALSE;                                {always default FALSE}
  429.     CASE event.what OF
  430.         keyDown, autoKey: BEGIN                            {check for <cr> or <enter>}
  431.             key := SignedByte(event.message);
  432.             IF key IN [kCR, kENTER] THEN BEGIN            {it was a <cr> or <enter>}
  433.                 GetDialogItem(dlg, ok, kind, h, r);
  434.                 HiliteControl(ControlHandle(h), kHilite);
  435.                 Delay(kHiliteDelay, finalTicks);
  436.                 HiliteControl(ControlHandle(h), kDeHilite);
  437.                 ListFilter := TRUE;                        {so we handle it}
  438.                 item := 1;                                {and make the first item hit}
  439.             END;
  440.         END;
  441.         mouseDown: BEGIN                                {we want mouseDowns}
  442.             GetPort(port);
  443.             SetPort(dlg);
  444.             loc := event.where;
  445.             GlobalToLocal(loc);                            {find where clicked}
  446.             GetDialogItem(dlg, dZoneList, kind, h, r);        {get rect for list}
  447.             IF PtInRect(loc, r) THEN BEGIN                {if clicked inside…}
  448.                 ListFilter := TRUE;                        {we take care of it}
  449.                 ignore := LClick(loc, event.modifiers,
  450.                                     gList);                {by passing click to list}
  451.             END;
  452.             SetPort(port);
  453.         END;
  454.     END;
  455. END; {ListFilter}
  456.  
  457.  
  458. {$S Main}
  459. PROCEDURE DoZoneList;
  460.  
  461. {Put up a modal dialog that shows a list of the zones on the net. Create the dialog
  462.  and list, call BuildZoneList to fill it, then wait for the user to click OK.}
  463.  
  464. VAR
  465.     dlg                        : DialogPtr;
  466.     item, kind                : INTEGER;
  467.     h                        : Handle;
  468.     r, rView, dataBounds    : Rect;
  469.     cSize                    : Point;
  470.     fi                        : FailInfo;
  471.     hor, ver                : INTEGER;
  472.     mfUPP                    : ModalFilterUPP;
  473.  
  474.     PROCEDURE CleanUp;
  475.     BEGIN
  476.         IF gList <> NIL THEN
  477.             LDispose(gList);                                {get rid of list}
  478.         IF dlg <> NIL THEN
  479.             DisposeDialog(dlg);                                {get rid of dialog}
  480.     END; {CleanUp}
  481.  
  482.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  483.     BEGIN
  484.         CleanUp;                                            {release junk}
  485.     END;
  486.  
  487. BEGIN
  488.     gList := NIL;                                            {init some important variables}
  489.     dlg := NIL;
  490.     CatchFailures(fi, HandleErr);
  491.  
  492.     dlg := GetNewDialog(rZoneDialog, NIL, POINTER(-1));        {create dialog}
  493.     FailNILMsg(dlg, eNoMemory);
  494.  
  495.     {We center the dialog horizontally and position it vertically one-third the
  496.      distance from the menu bar to the bottom of the main device. We do not
  497.      check for the dialog extending past the bottom of the device because we
  498.      know the dialog is not that big. You may wish to make that check.}
  499.     WITH dlg^.portRect DO BEGIN
  500.         hor := right - left;
  501.         ver := bottom - top;
  502.     END;
  503.     WITH qd.screenBits.bounds DO BEGIN
  504.         hor := ((right - left) - hor) DIV 2;
  505.         ver := (((bottom - top) - ver - GetMBarHeight) DIV 3) + GetMBarHeight;
  506.     END;
  507.     MoveWindow(dlg, hor, ver, FALSE);
  508.  
  509.     GetDialogItem(dlg, dDefault, kind, h, r);
  510.     h := @ZoneListDraw;                                        {connect drawing procedure}
  511.     SetDialogItem(dlg, dDefault, kind, h, r);
  512.     GetDialogItem(dlg, dZoneList, kind, h, r);
  513.     h := @ZoneListDraw;                                        {connect drawing procedure}
  514.     SetDialogItem(dlg, dZoneList, kind, h, r);
  515.     rView := r;
  516.     WITH rView DO
  517.         right := right - kScrollBarWidth;                    {adjust rectangle for scroll}
  518.     SetRect(dataBounds, 0, 0, 1, 0);                        {init to one-wide list}
  519.     SetPt(cSize, 0, 0);
  520.     gList := LNew(rView, dataBounds, cSize, 0, WindowPtr(dlg),
  521.                     FALSE, FALSE, FALSE, TRUE);                {create with vertical scroll}
  522.     FailNILMsg(gList, eNoMemory);
  523.  
  524.     if gMac.atDrvrVersNum >= 53 then
  525.         BuildZoneListPhase2
  526.     else
  527.         BuildZoneList;                                        {put the stuff into the list}
  528.  
  529.     SetPt(cSize, 0, 0);
  530.     LSetSelect(TRUE, cSize, gList);                            {select the first guy}
  531.     LSetDrawingMode(TRUE, gList);                            {turn on the list}
  532.     ShowWindow(dlg);                                        {turn on the dialog}
  533.     mfUPP := NewModalFilterProc(@ListFilter);
  534.     REPEAT
  535.         ModalDialog(mfUPP, item);                            {accept events}
  536.     UNTIL item = ok;                                        {until he presses 'ok'}
  537.     DisposeRoutineDescriptor(mfUPP);
  538.     CleanUp;
  539.  
  540.     Success(fi);
  541. END; {DoZoneList}
  542.  
  543.  
  544. {$S Main}
  545. FUNCTION DoCloseWindow(window: WindowPtr) : BOOLEAN;
  546. BEGIN
  547.     DoCloseWindow := TRUE;
  548.     IF IsDAWindow(window) THEN
  549.         CloseDeskAcc(WindowPeek(window)^.windowKind);
  550.     IF IsAppWindow(window) THEN
  551.         CloseWindow(window);
  552. END; {DoCloseWindow}
  553.  
  554.  
  555. {$S Initialize}
  556. PROCEDURE Initialize;
  557. VAR
  558.     menuBar            : Handle;
  559.     window            : WindowPtr;
  560.     ignoreError        : OSErr;
  561.     total, contig    : LongInt;
  562.     ignoreResult    : BOOLEAN;
  563.     event            : EventRecord;
  564.     count            : INTEGER;
  565.     fi                : FailInfo;
  566.  
  567.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  568.     BEGIN
  569.         IF error > 0 THEN
  570.             AlertUser(0, error)
  571.         ELSE
  572.             AlertUser(error, message);
  573.         ExitToShell;
  574.     END; {HandleErr}
  575.  
  576. BEGIN
  577.     gHasWaitNextEvent := TrapAvailable(_WaitNextEvent, ToolTrap);
  578.     gInBackground := FALSE;
  579.  
  580.     InitGraf(@qd.thePort);
  581.     InitFonts;
  582.     InitWindows;
  583.     InitMenus;
  584.     TEInit;
  585.     InitDialogs(NIL);
  586.     InitCursor;
  587.  
  588.     FOR count := 1 TO 3 DO
  589.         ignoreResult := EventAvail(everyEvent, event);
  590.  
  591.     CatchFailures(fi, HandleErr);
  592.  
  593.     FailOSErrMsg(MPPOpen, eAppleTalk);
  594.     FailOSErrMsg(ATPLoad, eAppleTalk);
  595.  
  596.     ignoreError := SysEnvirons(kSysEnvironsVersion, gMac);
  597.     IF gMac.machineType < 0 THEN
  598.         Failure(0, eWrongMachine);
  599.  
  600.     IF ORD(GetApplLimit) - ORD(ApplicationZone) < kMinHeap THEN
  601.         Failure(0, eSmallSize);
  602.  
  603.     PurgeSpace(total, contig);
  604.     IF total < kMinSpace THEN
  605.         Failure(0, eNoMemory);
  606.  
  607.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  608.     FailNILMsg(menuBar, eNoMemory);
  609.     SetMenuBar(menuBar);                    {install menus}
  610.     DisposeHandle(menuBar);
  611.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  612.     DrawMenuBar;
  613.  
  614.     Success(fi);
  615. END; {Initialize}
  616.  
  617.  
  618. {$S Main}
  619. PROCEDURE Terminate;
  620. VAR
  621.     aWindow    : WindowPtr;
  622.     closed    : BOOLEAN;
  623.  
  624. BEGIN
  625.     closed := TRUE;
  626.     REPEAT
  627.         aWindow := FrontWindow;                    {get the current front window}
  628.         IF aWindow <> NIL THEN
  629.             closed := DoCloseWindow(aWindow);    {close this window}
  630.     UNTIL (NOT closed) | (aWindow = NIL);        {do all windows}
  631.     IF closed THEN
  632.         ExitToShell;                            {exit if no cancellation}
  633. END; {Terminate}
  634.  
  635.  
  636. {$S Main}
  637. PROCEDURE AdjustMenus;
  638. VAR
  639.     window            : WindowPtr;
  640.     menu            : MenuHandle;
  641.  
  642. BEGIN
  643.     window := FrontWindow;
  644.  
  645.     menu := GetMenuHandle(mFile);
  646.     IF IsDAWindow(window) THEN                {we can allow desk accessories to be closed from the menu}
  647.         EnableItem(menu, iClose)
  648.     ELSE
  649.         DisableItem(menu, iClose);            {but not our traffic light window}
  650.  
  651.     menu := GetMenuHandle(mEdit);
  652.     IF IsDAWindow(window) THEN BEGIN        {a desk accessory might need the edit menu}
  653.         EnableItem(menu, iUndo);
  654.         EnableItem(menu, iCut);
  655.         EnableItem(menu, iCopy);
  656.         EnableItem(menu, iPaste);
  657.         EnableItem(menu, iClear);
  658.     END ELSE BEGIN                            {but we know we do not}
  659.         DisableItem(menu, iUndo);
  660.         DisableItem(menu, iCut);
  661.         DisableItem(menu, iCopy);
  662.         DisableItem(menu, iClear);
  663.         DisableItem(menu, iPaste);
  664.     END;
  665.  
  666. END; {AdjustMenus}
  667.  
  668.  
  669. {$S Main}
  670. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  671. VAR
  672.     menuID            : INTEGER;        {the resource ID of the selected menu}
  673.     menuItem        : INTEGER;        {the item number of the selected menu}
  674.     itemHit            : INTEGER;
  675.     daName            : Str255;
  676.     daRefNum        : INTEGER;
  677.     handledByDA        : BOOLEAN;
  678.     ignore            : BOOLEAN;
  679.     fi                : FailInfo;
  680.  
  681.     PROCEDURE HandleMenu(error: INTEGER; message: LongInt);
  682.     BEGIN
  683.         HiliteMenu(0);                {unhighlight what MenuSelect (or MenuKey) hilited}
  684.     END;
  685.  
  686. BEGIN
  687.     CatchFailures(fi, HandleMenu);
  688.     menuID := HiWord(menuResult);    {use built-ins (for efficiency)...}
  689.     menuItem := LoWord(menuResult);    {to get menu item number and menu number}
  690.     CASE menuID OF
  691.         mApple:
  692.             CASE menuItem OF
  693.                 iAbout:                {bring up alert for About}
  694.                     itemHit := Alert(rAboutAlert, NIL);
  695.                 OTHERWISE BEGIN        {all non-About items in this menu are DAs}
  696.                     GetMenuItemText(GetMenuHandle(mApple), menuItem, daName);
  697.                     daRefNum := OpenDeskAcc(daName);
  698.                 END;
  699.             END;
  700.         mFile:
  701.             CASE menuItem OF
  702.                 iNew:
  703.                     DoZoneList;
  704.                 iClose:
  705.                     ignore := DoCloseWindow(FrontWindow);
  706.                 iQuit:
  707.                     Terminate;
  708.             END;
  709.         mEdit:                        {call SystemEdit for DA editing & MultiFinder}
  710.             handledByDA := SystemEdit(menuItem-1);    {since we don't do any editing}
  711.     END;
  712.     Success(fi);
  713.     HiliteMenu(0);                    {cleanup}
  714. END; {DoMenuCommand}
  715.  
  716.  
  717. {$S Main}
  718. PROCEDURE DoEvent(event: EventRecord);
  719. VAR
  720.     part, err    : INTEGER;
  721.     window        : WindowPtr;
  722.     hit            : BOOLEAN;
  723.     key            : CHAR;
  724.     fi            : FailInfo;
  725.     aPoint        : Point;
  726.  
  727.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  728.     BEGIN
  729.         IF error > 0 THEN
  730.             AlertUser(0, error)
  731.         ELSE
  732.             AlertUser(error, message);
  733.         EXIT(DoEvent);
  734.     END; {HandleErr}
  735.  
  736. BEGIN
  737.     CatchFailures(fi, HandleErr);
  738.  
  739.     CASE event.what OF
  740.         mouseDown: BEGIN
  741.             part := FindWindow(event.where, window);
  742.             CASE part OF
  743.                 inMenuBar: BEGIN            {process the menu command}
  744.                     AdjustMenus;
  745.                     DoMenuCommand(MenuSelect(event.where));
  746.                 END;
  747.                 inSysWindow:                {let the system handle the mouseDown}
  748.                     SystemClick(event, window);
  749.                 inContent:;
  750.                 inDrag:;
  751.                 inGrow:;
  752.                 inZoomIn, inZoomOut:;
  753.             END;
  754.         END;
  755.         keyDown, autoKey: BEGIN                {check for menukey equivalents}
  756.             key := CHR(BAnd(event.message, charCodeMask));
  757.             IF BAnd(event.modifiers, cmdKey) <> 0 THEN    {Command key down}
  758.                 IF event.what = keyDown THEN BEGIN
  759.                     AdjustMenus;            {enable/disable/check menu items properly}
  760.                     DoMenuCommand(MenuKey(key));
  761.                 END;
  762.         END;                                {call DoActivate with the window and...}
  763.         activateEvt:;
  764.         updateEvt:;
  765.         {1.01 - It is not a bad idea to at least call DIBadMount in response
  766.          to a diskEvt, so that the user can format a floppy.}
  767.         diskEvt:
  768.             IF HiWord(event.message) <> noErr THEN BEGIN
  769.                 SetPt(aPoint, kDILeft, kDITop);
  770.                 err := DIBadMount(aPoint, event.message);
  771.             END;
  772.         kOSEvent:
  773.             CASE BAnd(BRotL(event.message, 8), 255) OF    {high byte of message}
  774.                 kSuspendResumeMessage: BEGIN
  775.                     gInBackground := BAnd(event.message, kResumeMask) = 0;
  776.                 END;
  777.             END;
  778.     END;
  779.  
  780.     Success(fi);
  781. END; {DoEvent}
  782.  
  783.  
  784. {$S Main}
  785. PROCEDURE EventLoop;
  786. VAR
  787.     cursorRgn    : RgnHandle;
  788.     gotEvent    : BOOLEAN;
  789.     event        : EventRecord;
  790.  
  791. BEGIN
  792.     cursorRgn := NewRgn;            {we’ll pass WNE an empty region the 1st time thru}
  793.  
  794.     REPEAT
  795.         IF gHasWaitNextEvent THEN    {put us 'asleep' forever under MultiFinder}
  796.             gotEvent := WaitNextEvent(everyEvent, event, MAXLONGINT, cursorRgn)
  797.         ELSE BEGIN
  798.             SystemTask;                {must be called if using GetNextEvent}
  799.             gotEvent := GetNextEvent(everyEvent, event);
  800.         END;
  801.         IF gotEvent THEN BEGIN
  802.             DoEvent(event);
  803.         END;
  804.     UNTIL FALSE;                    {loop forever; we quit through an ExitToShell}
  805. END; {EventLoop}
  806.  
  807.  
  808. PROCEDURE _DataInit; EXTERNAL;
  809.  
  810.  
  811. {$S Main}
  812. BEGIN
  813.     UnloadSeg(@_DataInit);        {note that _DataInit must not be in Main!}
  814.     MaxApplZone;                {expand the heap so code segments load at the top}
  815.  
  816.     InitSignals;
  817.     Initialize;                    {initialize the program}
  818.     UnloadSeg(@Initialize);        {note that Initialize must not be in Main!}
  819.  
  820.     EventLoop;                    {call the main event loop}
  821. END.
  822.